﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NetworkData.Spider
{
    /// <summary>
    /// Base class for parseing tag based files, such as HTML, HTTP headers
    /// or XML.
    /// 
    /// 
    /// This spider is copyright 2003 by Jeff Heaton. However, it is
    /// released under a Limited GNU Public License (LGPL). You may 
    /// use it freely in your own programs. For the latest version visit
    /// http://www.jeffheaton.com.
    ///	
    /// </summary>
    public class Parse : AttributeList
    {
        /// <summary>
        /// The source text that is being parsed.
        /// </summary>
        private string _mSource;

        /// <summary>
        /// The current position inside of the text that
        /// is being parsed.
        /// </summary>
        private int _mIdx;

        /// <summary>
        /// The most reciently parsed attribute delimiter.
        /// </summary>
        private char _mParseDelim;

        /// <summary>
        /// This most receintly parsed attribute name.
        /// </summary>
        private string _mParseName;

        /// <summary>
        /// The most reciently parsed attribute value.
        /// </summary>
        private string _mParseValue;

        /// <summary>
        /// The most reciently parsed tag.
        /// </summary>
        public string MTag;

        /// <summary>
        /// Determine if the specified character is whitespace or not.
        /// </summary>
        /// <param name="ch">A character to check</param>
        /// <returns>true if the character is whitespace</returns>
        public static bool IsWhiteSpace(char ch)
        {
            return ("\t\n\r ".IndexOf(ch) != -1);
        }


        /// <summary>
        /// Advance the index until past any whitespace.
        /// </summary>
        public void EatWhiteSpace()
        {
            while (!Eof())
            {
                if (!IsWhiteSpace(GetCurrentChar()))
                    return;
                _mIdx++;
            }
        }

        /// <summary>
        /// Determine if the end of the source text has been
        /// reached. 
        /// </summary>
        /// <returns>True if the end of the source text has been
        /// reached.</returns>
        public bool Eof()
        {
            return (_mIdx >= _mSource.Length);
        }

        /// <summary>
        /// Parse the attribute name.
        /// </summary>
        public void ParseAttributeName()
        {
            EatWhiteSpace();
            // get attribute name
            while (!Eof())
            {
                if (IsWhiteSpace(GetCurrentChar()) ||
                    (GetCurrentChar() == '=') ||
                    (GetCurrentChar() == '>'))
                    break;
                _mParseName += GetCurrentChar();
                _mIdx++;
            }

            EatWhiteSpace();
        }


        /// <summary>
        /// Parse the attribute value
        /// </summary>
        public void ParseAttributeValue()
        {
            if (_mParseDelim != 0)
                return;

            if (GetCurrentChar() == '=')
            {
                _mIdx++;
                EatWhiteSpace();
                if ((GetCurrentChar() == '\'') ||
                    (GetCurrentChar() == '\"'))
                {
                    _mParseDelim = GetCurrentChar();
                    _mIdx++;
                    while (GetCurrentChar() != _mParseDelim)
                    {
                        _mParseValue += GetCurrentChar();
                        _mIdx++;
                    }
                    _mIdx++;
                }
                else
                {
                    while (!Eof() &&
                        !IsWhiteSpace(GetCurrentChar()) &&
                        (GetCurrentChar() != '>'))
                    {
                        _mParseValue += GetCurrentChar();
                        _mIdx++;
                    }
                }
                EatWhiteSpace();
            }
        }

        /// <summary>
        /// Add a parsed attribute to the collection.
        /// </summary>
        public void AddAttribute()
        {
            var a = new Attribute(_mParseName,
                _mParseValue, _mParseDelim);
            Add(a);
        }

        /// <summary>
        /// Get the current character that is being parsed.
        /// </summary>
        /// <returns></returns>
        public char GetCurrentChar()
        {
            return GetCurrentChar(0);
        }

        /// <summary>
        /// Get a few characters ahead of the current character.
        /// </summary>
        /// <param name="peek">How many characters to peek ahead for.</param>
        /// <returns>The character that was retrieved.</returns>
        public char GetCurrentChar(int peek)
        {
            if ((_mIdx + peek) < _mSource.Length)
                return _mSource[_mIdx + peek];
            return (char)0;
        }

        /// <summary>
        /// Obtain the next character and advance the index by one.
        /// </summary>
        /// <returns>The next character</returns>
        public char AdvanceCurrentChar()
        {
            return _mSource[_mIdx++];
        }

        /// <summary>
        /// Move the index forward by one.
        /// </summary>
        public void Advance()
        {
            _mIdx++;
        }


        /// <summary>
        /// The last attribute name that was encountered.
        /// </summary>
        public string ParseName
        {
            get
            {
                return _mParseName;
            }

            set
            {
                _mParseName = value;
            }
        }

        /// <summary>
        /// The last attribute value that was encountered.
        /// </summary>
        public string ParseValue
        {
            get
            {
                return _mParseValue;
            }

            set
            {
                _mParseValue = value;
            }
        }

        /// <summary>
        /// The last attribute delimeter that was encountered.
        /// </summary>
        public char ParseDelim
        {
            get
            {
                return _mParseDelim;
            }

            set
            {
                _mParseDelim = value;
            }
        }

        /// <summary>
        /// The text that is to be parsed.
        /// </summary>
        public string Source
        {
            get
            {
                return _mSource;
            }

            set
            {
                _mSource = value;
            }
        }
    }
}
